#include "viewer.h"

#include <board.h>
#include <lcd/lcdd.h>
#include <lcd/draw.h>
#include <tsd/tsd.h>
#include <tsd/inarea.h>
#include <pit/pitd.h>

#define _NUMOF_FRAMES   3 // must be bigger than 1

static unsigned char *frames[_NUMOF_FRAMES] = {
        [0 ... (_NUMOF_FRAMES-1)] = (unsigned char *) (AT91C_EBI_SDRAM + 0x01200000)
};

static unsigned char ** volatile curr_frame;
static unsigned char ** volatile last_frame;

#define _EXIT_SIZE 60

static inarea_rect_t exit = { .x = 0, .y = 0, .width = _EXIT_SIZE, .height = _EXIT_SIZE };
static inarea_rect_t picture [2] = {
        { .x = _EXIT_SIZE, .y = 0, .width = BOARD_LCD_WIDTH - _EXIT_SIZE, .height = BOARD_LCD_HEIGHT},
        { .x = 0, .y = _EXIT_SIZE, .width = _EXIT_SIZE, .height = BOARD_LCD_HEIGHT - _EXIT_SIZE }
};

static void draw_exit (color_t back, color_t front) {
    LCDD_DrawCircle(*curr_frame,
            _EXIT_SIZE / 2,
            _EXIT_SIZE / 2,
            (_EXIT_SIZE - 10) / 2,
            back);
    LCDD_DrawCircle(*curr_frame,
            _EXIT_SIZE / 2,
            _EXIT_SIZE / 2,
            (_EXIT_SIZE - 20) / 2,
            front);
}

static int _selected;
static volatile int state;
#define _PLAY   1
#define _PAUSE  2
#define _EXIT   3

#define _PAUSE_IF_PLAYING() \
    if (!_selected && state == _PLAY) { \
        state = _PAUSE; \
        return; \
    }

#define _CHECK_SELECTION()  { if (!_selected) return; _selected = 0; }

static void exit_pressed (inarea_rect_t * pRect) {
    _PAUSE_IF_PLAYING();
    _selected = 1;
    draw_exit(COLOR_BLACK, COLOR_WHITE);
}
static void exit_moved_out (inarea_rect_t * pRect) {
    _CHECK_SELECTION();
    draw_exit(COLOR_RED, COLOR_TOMATO);
}
static void exit_release (inarea_rect_t * pRect) {
    _CHECK_SELECTION();
    state = _EXIT;
}

static void pause (inarea_rect_t * pRect) {
    _PAUSE_IF_PLAYING();
    _selected = 1;
}
static void play (inarea_rect_t * pRect) {
    if (_selected && state == _PAUSE) {
        _selected = 0;
        state = _PLAY;
    }
}

static void _viewer_handler (void) {
    static int paused = 0;
    unsigned char **next_frame;

    if (paused) {
        if (state == _PLAY) {
            paused = 0;
        } else if (state == _PAUSE) {
            goto _touch_handling;
        } else {
            return;
        }
    } else if (state != _PLAY) {
        if (state == _PAUSE) {
            paused = 1;
            draw_exit(COLOR_RED, COLOR_TOMATO);
            goto _touch_handling;
        }
        if (state == _EXIT) {
            return;
        }
    }

    if (curr_frame == last_frame) {
        // Ups! It's buffering...
    } else {
        next_frame = curr_frame;
        ++next_frame;
        if (next_frame == frames + _NUMOF_FRAMES) {
            next_frame = frames;
        }
        LCDD_DisplayBuffer(*next_frame);
        curr_frame = next_frame;
    }

    _touch_handling:
    TSD_Update_PenState();
    return;
}

int viewer_open (FIL* file) {
    unsigned char *oldBuff;
    color_t colour = 0x3;
    unsigned char **next_frame;
    int i, f = 750;

    curr_frame = frames;
    LCDD_Fill(*curr_frame, COLOR_BLACK);
    oldBuff = LCDD_DisplayBuffer(*curr_frame);
    last_frame = curr_frame;
    next_frame = frames + 1;

    TSD_InareaAdd(exit_pressed, exit_moved_out, exit_release, &exit, 1);
    TSD_InareaAdd(pause, 0, play, picture, 2);
    PITD_Configure(_viewer_handler, 40000, 1);
    TSD_SetPitPeriod(40);

    _selected = 0;
    state = _PLAY;
    PITD_Start();

    while (state != _EXIT) {
        if (next_frame == curr_frame) {
            continue;
        }
        // process,
        LCDD_Fill(*next_frame, colour);
        colour <<= 1;
        if (colour > 0xffffff) {
            colour = (colour & 0xffffff) | 1;
        }
        for (i = 0; i < 10000; ++i); // processing...
        last_frame = next_frame;
        --f;
        if (f == 0) {
            while (curr_frame != last_frame);
            break;
        }
        // ... processed
        ++next_frame;
        if (next_frame == frames + _NUMOF_FRAMES) {
            next_frame = frames;
        }
    }

    PITD_Stop();
    TSD_InareaRemoveAll();

    LCDD_DisplayBuffer(oldBuff);

    return 0;
}

void viewer_init (void) {
    int f;
    for (f = 0; f < _NUMOF_FRAMES; ++f) {
        frames[f] += f * BOARD_LCD_WIDTH * BOARD_LCD_HEIGHT * 3;
    }
}
